package com.whfc.mach.service.impl;

import cn.hutool.core.date.DateTime;
import com.alibaba.nacos.api.config.annotation.NacosValue;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.whfc.common.enums.DeviceType;
import com.whfc.common.enums.FenceType;
import com.whfc.common.exception.BizException;
import com.whfc.common.file.FileHandler;
import com.whfc.common.file.FilePathConfig;
import com.whfc.common.result.ListData;
import com.whfc.common.result.PageData;
import com.whfc.common.result.PageVO;
import com.whfc.common.result.ResultEnum;
import com.whfc.common.third.map.MapApi;
import com.whfc.common.third.map.MapLoc;
import com.whfc.common.util.*;
import com.whfc.fuum.dto.AppCorpDTO;
import com.whfc.fuum.dto.SysMachListDTO;
import com.whfc.fuum.dto.SysMachTypeDTO;
import com.whfc.fuum.service.AppCorpService;
import com.whfc.fuum.service.SysDeptService;
import com.whfc.fuum.service.SysMachTypeService;
import com.whfc.hdc.service.AppDeviceService;
import com.whfc.mach.dao.*;
import com.whfc.mach.dto.Gps;
import com.whfc.mach.dto.*;
import com.whfc.mach.entity.*;
import com.whfc.mach.enums.BindFlag;
import com.whfc.mach.enums.MaintainState;
import com.whfc.mach.enums.NetState;
import com.whfc.mach.enums.WorkState;
import com.whfc.mach.manager.*;
import com.whfc.mach.manager.util.MachSegmentUtil;
import com.whfc.mach.manager.util.MachUtil;
import com.whfc.mach.param.MachPageDataQueryParam;
import com.whfc.mach.param.MachQueryParam;
import com.whfc.mach.service.AppMachDataService;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author sun_guodong
 * @description
 * @date 2020-11-11
 */
@DubboService(interfaceClass = AppMachDataService.class, version = "1.0.0", timeout = 10000)
public class AppMachDataServiceImpl implements AppMachDataService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private AppMachDataMapper appMachDataMapper;

    @Autowired
    private AppMachMapper appMachMapper;

    @Autowired
    private AppMachOperEmpMapper appMachOperEmpMapper;

    @Autowired
    private AppMachImgMapper appMachImgMapper;

    @Autowired
    private MachDataManager machDataManager;

    @Autowired
    private MachDataLogManager machDataLogManager;

    @Autowired
    private AppMachDayMapper appMachDayMapper;

    @Autowired
    private AppMachDeviceMapper appMachDeviceMapper;

    @Autowired
    private AppDeviceMachDataMapper appDeviceMachDataMapper;

    @Autowired
    private AppMachOilRecordMapper appMachOilRecordMapper;

    @Autowired
    private AppMachOilLogMapper appMachOilLogMapper;

    @Autowired
    private MachConfigManager machConfigManager;

    @Autowired
    private MachMgr machMgr;

    @Autowired
    private ObdDataMgr obdDataMgr;

    @Autowired
    private AppMaintainMapper maintainMapper;

    @DubboReference(interfaceClass = SysMachTypeService.class, version = "1.0.0")
    private SysMachTypeService sysMachTypeService;

    @DubboReference(interfaceClass = SysDeptService.class, version = "1.0.0")
    private SysDeptService sysDeptService;

    @DubboReference(interfaceClass = AppCorpService.class, version = "1.0.0")
    private AppCorpService appCorpService;

    @Autowired
    private MapApi mapApi;

    @Autowired
    private FileHandler fileHandler;

    @Autowired
    private FilePathConfig filePathConfig;

    @NacosValue(value ="${mach.info.url}", autoRefreshed = true)
    private String machInfoUrl;

    /**
     * 设备二维码在oss的目录
     */
    private static final String QR_PATH = "mach/qr";

    @Override
    public PageData<AppMachDTO> list(MachPageDataQueryParam param) {
        logger.info("设备列表服务,param:{}", param.toString());
        List<Integer> deptIds = param.getDeptIds();
        Integer pageNum = param.getPageNum();
        Integer pageSize = param.getPageSize();
        Integer bindFlag = param.getBindFlag();
        Integer corpId = param.getCorpId();
        String keyword = param.getKeyword();
        Integer machType = param.getMachType();
        Integer netState = param.getNetState();
        Integer state = param.getState();
        Integer workState = param.getWorkState();
        Integer machId = param.getMachId();
        List<Integer> ids = new ArrayList<>();
        if (machType != null){
            SysMachTypeDTO sysMachTypeDTO = sysMachTypeService.getMachTypeById(machType);
            if (sysMachTypeDTO.getPid() != 0){
                ids.add(machType);
            }else {
                ListData<SysMachListDTO> listData = sysMachTypeService.machLists(sysMachTypeDTO.getMachTypeId());
                List<SysMachListDTO> list = listData.getList();
                if (list != null && !list.isEmpty() && list.get(0).getChildren() != null)
                    ids = list.get(0).getChildren().stream().map(SysMachListDTO::getMachTypeId).collect(Collectors.toList());
            }
        }

        PageHelper.startPage(pageNum, pageSize);
        List<AppMachDTO> list = appMachMapper.selectMachList(deptIds, bindFlag, corpId, keyword, ids, netState, state, workState, machId);
        PageHelper.clearPage();
        if (list.isEmpty()) {
            return PageUtil.pageData(PageInfo.of(list));
        }
        // 查找组织机构名称
        Map<Integer, String> deptNameMap = sysDeptService.getDeptNameMap(deptIds);

        // 查找设备图片
        List<Integer> machIds = list.stream().map(mach -> mach.getMachId()).collect(Collectors.toList());
        List<AppMachImg> machImgList = appMachImgMapper.selectImgListByMachIds(machIds);
        Map<Integer, List<String>> imgMap = this.list2Map(machImgList);

        //查找持有单位
        List<Integer> corpIds = list.stream().map(mach -> mach.getCorpId()).collect(Collectors.toList());
        List<AppCorpDTO> appCorpDTOS = appCorpService.list(corpIds);
        Map<Integer, String> corpMap = this.list3Map(appCorpDTOS);

        for (AppMachDTO appMachDTO : list) {
            appMachDTO.setImgList(imgMap.get(appMachDTO.getMachId()));
            appMachDTO.setDeptName(deptNameMap.get(appMachDTO.getDeptId()));
            appMachDTO.setMaintainState(maintainMapper.countByMachId(appMachDTO.getMachId()) == 1 ? MaintainState.UNMAINTAIN.getValue() : MaintainState.MAINTAIN.getValue());
            appMachDTO.setCorpName(corpMap.get(appMachDTO.getCorpId()));
        }

        return PageUtil.pageData(PageInfo.of(list));
    }

    @Override
    public ListData<AppMachDTO> list(List<Integer> deptIds, String keyword, Integer deviceType, Integer state, Integer bindFlag, Integer machType) throws BizException {
        logger.info("查找设备列表(不分页),deptIds:{},keyword:{},deviceType:{},state:{},bindFlag:{},machType:{}", deptIds, keyword, deviceType, state, bindFlag, machType);
        List<AppMachDTO> list = appMachMapper.selectAvailableMachDTO(deptIds, keyword, deviceType, state, bindFlag, machType);
        return new ListData<>(list);
    }

    @Override
    public MapMachDTO getMapList(Integer deptId, List<Integer> deptIds, List<Integer> corpIds, List<Integer> machTypeIds, String keyword) {
        logger.info("获取地图页设备列表服务,deptIds:{},corpIds:{},machTypeIds:{},keyword:{}", deptIds, corpIds, machTypeIds, keyword);
        List<AppMachDTO> list = appMachDataMapper.selectMapMachList(deptIds, corpIds, machTypeIds, keyword, BindFlag.BIND.getValue(), null, null);

        List<AppMachDTO> mapList = new ArrayList<>();
        Date now = new Date();
        Integer timeOut = machConfigManager.getTimeOut(deptId);
        Date lastTime = DateUtil.addMinutes(now, -timeOut);

        for (AppMachDTO appMachDTO : list) {
            String icon = sysMachTypeService.getIcon(appMachDTO.getMachType(), appMachDTO.getNetState(), appMachDTO.getWorkState());
            appMachDTO.setIcon(icon);

            Date time = appMachDTO.getTime();
            if (time == null || time.before(lastTime)) {
                continue;
            }
//            Double lng = appMachDTO.getLng();
//            Double lat = appMachDTO.getLat();
//            String polygonWKT = machConfigManager.getPolygonWKT(deptId, appMachDTO.getMachTypeName());
//            boolean inChina = PositionUtil.contains(FenceType.POLYGON.value(), polygonWKT, null, lat, lng);
//            if (inChina) {
                mapList.add(appMachDTO);
//            }
        }

        MapMachDTO data = new MapMachDTO();
        data.setList(list);
        data.setMapList(mapList);
        return data;
    }

    @Override
    public PageData<AppMachDTO> dataList(List<Integer> deptIds, Integer pageNum, Integer pageSize, String keyword, Integer bindFlag, Integer netState, Integer workState) {
        logger.info("获取设备数据列表服务,deptIds:{},pageNum:{},pageSize:{},keyword:{},bindFlag:{},netState:{}", deptIds, pageNum, pageSize, keyword, bindFlag, netState);
        PageHelper.startPage(pageNum, pageSize);
        List<AppMachDTO> list = appMachDataMapper.selectMapMachList(deptIds, null, null, keyword, bindFlag, netState, workState);
        return PageUtil.pageData(PageInfo.of(list));
    }

    @Override
    public List<AppMachDTO> dataList(List<Integer> deptIds, String keyword, Integer deviceType) {
        logger.info("获取设备数据列表(不分页)服务,deptIds:{},keyword:{},deviceType:{}", deptIds, keyword, deviceType);
        List<AppMachDTO> list = appMachDataMapper.selectBindMachList(deptIds, keyword, deviceType);
        return list;
    }

    @Override
    public PageData<AppMachDTO> deviceList(List<Integer> deptIds, Integer pageNum, Integer pageSize, String keyword, Date startTime, Date endTime) {
        logger.info("机械云管家列表服务,deptIds:{},pageNum:{},pageSize:{},keyword:{},startTime:{},endTime:{}", deptIds, pageNum, pageSize, keyword, startTime, endTime);
        PageHelper.startPage(pageNum, pageSize);
        List<AppMachDTO> list = appMachDataMapper.selectMachData(deptIds, keyword, startTime, endTime);
        for (AppMachDTO appMachDTO : list) {
            // 计算当前油量
            Oil oil = machMgr.computeRemainOil(appMachDTO.getMachId(), appMachDTO.getOilPos());
            appMachDTO.setRemainOil(oil.getRemainOil());
            appMachDTO.setRemainOilPercent(oil.getRemainOilPercent());
        }
        return PageUtil.pageData(PageInfo.of(list));
    }

    @Override
    public AppMachDTO getMachInfo(Integer machId) {
        logger.info("获取设备基本信息服务,machId:{}", machId);
        AppMachDTO machDTO = appMachMapper.selectMachInfo(machId);
        if (machDTO != null) {
            // 计算当前油量
            Oil oil = machMgr.computeRemainOil(machId, machDTO.getOilPos());
            machDTO.setRemainOil(oil.getRemainOil());
            machDTO.setRemainOilPercent(oil.getRemainOilPercent());

            // 查找操作手信息
            List<MachOperEmpDTO> operators = appMachOperEmpMapper.selectByMachId(machId);
            machDTO.setOperEmpList(operators);

            // 查找设备图片
            List<String> imgList = appMachImgMapper.selectImgListByMachId(machId);
            machDTO.setImgList(imgList);

            // 组织机构名称
            String deptName = sysDeptService.getDeptName(machDTO.getDeptId());
            machDTO.setDeptName(deptName);
            if (machDTO.getCorpId() != null) {
                AppCorpDTO appCorpDTO = appCorpService.detail(machDTO.getCorpId());
                if (appCorpDTO != null) {
                    machDTO.setCorpName(appCorpDTO.getCorpName());
                }
            }
        }
        return machDTO;
    }

    @Override
    public String getMachQr(Integer machId) {
        logger.info("获取设备二维码服务,machId:{}", machId);
        String upload = "";
        AppMach machDTO = appMachMapper.selectByPrimaryKey(machId);
        if (machDTO == null) {
            logger.info("未找到设备信息,machId:{}", machId);
            return upload;
        }
        upload = machDTO.getQr();
        if (!StringUtils.isBlank(upload)) {
            return upload;
        }

        try {
            String logo = machConfigManager.getLogo(machDTO.getHoldDeptId());
            InputStream inputStream1 = Base64Util.getInputStream(logo);
            if (inputStream1 == null) {
                //ClassPathResource resource = new ClassPathResource("log.jpg");
                ClassPathResource resource = new ClassPathResource("120.png");
                inputStream1 = resource.getInputStream();
            }
            String machCode = machDTO.getMachCode();
            String fileName = "ags" + machId;
            File tempFile = File.createTempFile(fileName, ".jpg");
            QRCodeUtil.encode(machInfoUrl + machId, machCode, inputStream1, tempFile.getAbsolutePath());

            // 上传oss
            FileInputStream inputStream = new FileInputStream(tempFile);
            Map<String, String> headers = new HashMap<>();
            headers.put("cache-control", "no-cache");
            String path = filePathConfig.getFilePath(QR_PATH, fileName, "jpg");
            upload = fileHandler.upload(path, inputStream, headers);

            // 更新app_mach中的qr信息
            appMachMapper.updateQrByMachId(machId, upload);
        } catch (Exception e) {
            logger.error("生成设备二维码出错了,{}", e);
        }

        return upload;
    }

    @Override
    public PageData<AppMachDTO> getWxMachList(MachPageDataQueryParam param) {
        logger.info("获取小程序设备列表(分页)服务,param:{}", param.toString());
        List<Integer> deptIds = param.getDeptIds();
        Integer bindFlag = param.getBindFlag();
        String keyword = param.getKeyword();
        List<Integer> corpIds = param.getCorpIds();
        List<Integer> machTypeIds = param.getMachTypeIds();
        Integer pageNum = param.getPageNum();
        Integer pageSize = param.getPageSize();

        PageHelper.startPage(pageNum, pageSize);
        List<AppMachDTO> list = appMachMapper.selectWxMachList(deptIds, machTypeIds, corpIds, bindFlag, keyword);
        PageHelper.clearPage();
        return PageUtil.pageData(PageInfo.of(list));
    }

    @Override
    public List<AppMachDTO> getWxMachList(MachQueryParam param) {
        logger.info("获取小程序设备列表(不分页)服务,param:{}", param.toString());
        List<Integer> deptIds = param.getDeptIds();
        List<Integer> corpIds = param.getCorpIds();
        List<Integer> machTypeIds = param.getMachTypeIds();

        List<AppMachDTO> list = appMachMapper.selectWxMachList(deptIds, machTypeIds, corpIds, BindFlag.BIND.getValue(), null);
        return list;
    }

    @Override
    public MachDataStatDTO machNumstatic(MachQueryParam param) {
        logger.info("统计设备数量服务,param:{}", param.toString());
        List<Integer> deptIds = param.getDeptIds();
        String keyword = param.getKeyword();
        List<Integer> corpIds = param.getCorpIds();
        List<Integer> machTypeIds = param.getMachTypeIds();

        int bindNum = 0;
        int unbindNum = 0;
        int onlineNum = 0;
        int workNum = 0;
        int idleNum = 0;
        int stopNum = 0;
        List<AppMachDTO> list = appMachDataMapper.selectMapMachListV2(deptIds, corpIds, machTypeIds, keyword, null, null, null);
        for (AppMachDTO appMachDTO : list) {
            Integer netState = appMachDTO.getNetState();
            Integer bindFlag = appMachDTO.getBindFlag();
            Integer workState = appMachDTO.getWorkState();
            if (BindFlag.BIND.getValue().equals(bindFlag)) {
                bindNum++;
            }
            if (BindFlag.UNBIND.getValue().equals(bindFlag)) {
                unbindNum++;
            }
            if (NetState.ONLINE.getValue().equals(netState)) {
                onlineNum++;
                if (WorkState.IDLE.getValue().equals(workState)) {
                    idleNum++;
                }
                if (WorkState.RUN.getValue().equals(workState)) {
                    workNum++;
                }
                if (WorkState.STOP.getValue().equals(workState)) {
                    stopNum++;
                }
            }
        }

        MachDataStatDTO data = new MachDataStatDTO();
        data.setBindNum(bindNum);
        data.setUnbindNum(unbindNum);
        data.setTotalNum(bindNum + unbindNum);
        data.setOnlineNum(onlineNum);
        data.setOfflineNum(bindNum - onlineNum);
        data.setWorkNum(workNum);
        data.setStopNum(stopNum);
        data.setIdleNum(idleNum);
        return data;
    }

    @Override
    public MachLogNumDTO dayReportDate(Integer deptId, Date date) throws BizException {
        MachCurveMapDayDTO machCurveMapDayDTO = appMachDayMapper.selectDayMachNumByDetId(deptId, date);
        MachLogNumDTO machLogNumDTO = appMachMapper.selectTodayByDeptId(deptId, date);


        if (machLogNumDTO == null) {
            machLogNumDTO = new MachLogNumDTO();
            machLogNumDTO.setTodayNetInNum(0);
            machLogNumDTO.setTodayNetOffNum(0);
        }
        Integer totalMachNum = 0;
        Double workTimes = 0D;
        Double idleTimes = 0D;
        Double efficExp = 0D;
        Double oilWear = 0D;
        if (machCurveMapDayDTO != null) {
            if (machCurveMapDayDTO.getTotalMachNum() != null) {
                totalMachNum = machCurveMapDayDTO.getTotalMachNum();
            }
            if (machCurveMapDayDTO.getWorkTimes() != null) {
                workTimes = machCurveMapDayDTO.getWorkTimes();
            }
            if (machCurveMapDayDTO.getIdleTimes() != null) {
                idleTimes = machCurveMapDayDTO.getIdleTimes();
            }
            if (machCurveMapDayDTO.getEfficExp() != null) {
                efficExp = machCurveMapDayDTO.getEfficExp();
            }
            if (machCurveMapDayDTO.getOilWear() != null) {
                oilWear = machCurveMapDayDTO.getOilWear();
            }
        }
        machLogNumDTO.setAvgWorkTimes(workTimes);
        machLogNumDTO.setAvgIdleTimes(idleTimes);
        machLogNumDTO.setTotalIdleTimes(MathUtil.round(idleTimes * totalMachNum, 2));
        machLogNumDTO.setTotalWorkTimes(MathUtil.round(workTimes * totalMachNum, 2));
        machLogNumDTO.setAvgOilWear(oilWear);
        machLogNumDTO.setAvgEfficExp(efficExp);
        machLogNumDTO.setTotalMachNum(totalMachNum);
        return machLogNumDTO;
    }


    @Override
    public List<AppMachGpsDTO> getAllMachGpsList(List<Integer> deptIds, Date date) {
        logger.info("查找多台设备的轨迹,deptIds:{},date:{}", deptIds, date);
        // 查询当天的设备列表
        List<AppMachDTO> machDTOList = appMachDayMapper.selectMachDTOList(deptIds, date);

        // 查询设备轨迹
        if (machDTOList.size() > 0) {
            List<AppMachGpsDTO> machGpsDTOList = new ArrayList<>(machDTOList.size());
            for (AppMachDTO machDTO : machDTOList) {
                Integer machId = machDTO.getId();
                // 查询单台设备轨迹
                List<Gps> gpsList = machDataLogManager.getMachGpsByDate(machId, date);

                // 设备图标
                String machTypeIcon = sysMachTypeService.getIcon(machDTO.getMachType(), WorkState.RUN.getValue());

                AppMachGpsDTO machGpsDTO = new AppMachGpsDTO();
                machGpsDTO.setGpsList(gpsList);
                machGpsDTO.setMachId(machId);
                machGpsDTO.setMachCode(machDTO.getMachCode());
                machGpsDTO.setMachTypeName(machDTO.getMachTypeName());
                machGpsDTO.setIcon(machTypeIcon);
                machGpsDTOList.add(machGpsDTO);
            }
            return machGpsDTOList;
        }
        return Collections.emptyList();
    }

    @Override
    public AppMachGpsDTO gpsLog(Integer machId, Date date) {
        logger.info("查找设备的运动轨迹服务,machId:{},date:{}", machId, date);

        AppMachDTO machDTO = appMachMapper.selectMachDTOById(machId);
        if (machDTO == null) {
            throw new BizException(ResultEnum.PARAM_ERROR.getCode(), "设备不存在!");
        }
        List<Gps> gpsList = machDataLogManager.getMachGpsByDate(machId, date);

        AppMachGpsDTO appMachGpsDTO = new AppMachGpsDTO();
        BeanUtils.copyProperties(machDTO, appMachGpsDTO);
        appMachGpsDTO.setGpsList(gpsList);
        return appMachGpsDTO;
    }

    @Override
    public PageData<AppMachDataLogDTO> gpsLogPage(Integer machId, Date date, Integer pageNum, Integer pageSize) {
        logger.info("查找设备的运动轨迹服务,machId:{},date:{}", machId, date);
        DateTime startTime = cn.hutool.core.date.DateUtil.beginOfDay(date);
        DateTime endTime = cn.hutool.core.date.DateUtil.endOfDay(date);

        PageData<AppMachDataLogDTO> pageData = machDataLogManager.getDeviceDataLog(machId, startTime, endTime, pageNum, pageSize);
//        AppMachDTO machDTO = appMachMapper.selectMachDTOById(machId);
//        if (machDTO == null) {
//            throw new BizException(ResultEnum.PARAM_ERROR.getCode(), "设备不存在!");
//        }
//        List<Gps> gpsList = machDataLogManager.getMachGpsByDate(machId, date,pageNum,pageSize);
//
//        AppMachGpsDTO appMachGpsDTO = new AppMachGpsDTO();
//        BeanUtils.copyProperties(machDTO, appMachGpsDTO);
//        appMachGpsDTO.setGpsList(gpsList);
        return pageData;
    }
    @Override
    public Gps gpsLatest(Integer machId) {
        logger.info("获取设备最新位置服务,machId:{}", machId);
        Gps gps = new Gps();
        AppMachData appMachData = appMachDataMapper.selectByMachId(machId);
        if (appMachData != null) {
            gps.setLng(appMachData.getLng());
            gps.setLat(appMachData.getLat());
        }
        return gps;
    }


    @Override
    public AppMachWorkHourDayDTO getMachWorkHourDayData(Integer machId, Date date) {
        logger.info("查询设备一天工时服务服务,machId:{},date:{}", machId, date);
        Date now = new Date();
        Date today = DateUtil.getDate(now);
        Date dateBegin = DateUtil.getDateBegin(date);
        Date dateEnd = today.getTime() == date.getTime() ? now : DateUtil.getDateEnd(date);

        // 数据分段
        List<AppMachDaySegDTO> machDaySegDTOS = machDataManager.getMachDaySegList(machId, date);

        // 合并分段
        if (machDaySegDTOS.size() > 0) {
            // 补充头部分段
            this.addHeadSegment(machId, machDaySegDTOS, date);

            // 补充尾部分段
            this.addTailSegment(machId, machDaySegDTOS, date);
        } else {
            // 没有数据分段,当离线处理
            AppMachDaySegDTO appMachDaySegDTO = MachSegmentUtil.buildMachDaySegment(dateBegin, dateEnd, null, NetState.OFFLINE.getValue());
            machDaySegDTOS.add(appMachDaySegDTO);
        }

        // 合并相同分段
        MachSegmentUtil.mergeSameStateSegment(machDaySegDTOS);

        double workTimes = 0D;
        double idleTimes = 0D;
        double stopTimes = 0D;
        double offlineTimes = 0D;
        for (AppMachDaySegDTO appMachDaySegDTO : machDaySegDTOS) {
            // 怠速
            if (WorkState.IDLE.getValue().equals(appMachDaySegDTO.getWorkState())) {
                idleTimes += appMachDaySegDTO.getTimes();
            }
            // 工作
            if (WorkState.RUN.getValue().equals(appMachDaySegDTO.getWorkState())) {
                workTimes += appMachDaySegDTO.getTimes();
            }
            // 停机
            if (WorkState.STOP.getValue().equals(appMachDaySegDTO.getWorkState())) {
                stopTimes += appMachDaySegDTO.getTimes();
            }
            // 离线
            if (NetState.OFFLINE.getValue().equals(appMachDaySegDTO.getNetState())) {
                offlineTimes += appMachDaySegDTO.getTimes();
            }
        }

        AppMachWorkHourDayDTO workHour = new AppMachWorkHourDayDTO();
        workHour.setOfflineTimes(offlineTimes);
        workHour.setStopTimes(stopTimes);
        workHour.setWorkTimes(workTimes);
        workHour.setIdleTimes(idleTimes);
        workHour.setTimesArr(machDaySegDTOS);
        return workHour;
    }

    @Override
    public MachRunDataDTO getMachWorkAndOilData(Integer machId, Date startDate, Date endDate) {
        logger.info("查询一台设备一段时间的工时和油耗服务,machId:{},startDate:{},endDate:{}", machId, startDate, endDate);
        MachRunDataDTO machRunDataDTO = new MachRunDataDTO();
        List<MachCurveMapDayDTO> list = appMachDayMapper.selectSingleMachCurveMapDayList(machId, startDate, endDate);
        Double workTimes = 0D;
        Double idleTimes = 0D;
        Double oilWear = 0D;
        for (MachCurveMapDayDTO wxMachCurveMapDayDTO : list) {
            workTimes += wxMachCurveMapDayDTO.getWorkTimes();
            idleTimes += wxMachCurveMapDayDTO.getIdleTimes();
            oilWear += wxMachCurveMapDayDTO.getOilWear();
        }
        Integer size = list.size();
        if (size > 0) {
            workTimes = MathUtil.round(1.0 * workTimes / size, 1);
            idleTimes = MathUtil.round(1.0 * idleTimes / size, 1);
            oilWear = MathUtil.round(1.0 * oilWear / size, 1);
        }
        machRunDataDTO.setDateList(list);
        machRunDataDTO.setOilWear(oilWear);
        machRunDataDTO.setWorkTimes(workTimes);
        machRunDataDTO.setIdleTimes(idleTimes);

        return machRunDataDTO;
    }

    @Override
    public MachRunDataDTO getOilData(Integer machId, Date startDate, Date endDate) throws BizException {
        logger.info("获取设备油耗,machId:{},startDate:{},endDate:{}", machId, startDate, endDate);

        // 查找设备的基本信息
        AppMachInfo machInfo = this.getAndcheckMachOilTank(machId);

        //油位曲线
        Date startTime = DateUtil.getDateBegin(startDate);
        Date endTime = DateUtil.getDateEnd(endDate);
        List<Oil> oilList = machDataLogManager.getMachOilByTime(machId, startTime, endTime);
        List<Oil> remainOilList = machMgr.computeRemainOil(machInfo, oilList);

        //每日油耗
        List<OilDay> dayOilList = appMachDayMapper.selectOilDayListByMachId(machId, startDate, endDate);

        //总油耗
        Double oilWearTotal = 0D;
        Double oilAddTotal = 0D;
        for (OilDay oilDay : dayOilList) {
            oilWearTotal += oilDay.getOilWear();
            oilAddTotal += oilDay.getOilAdd();
        }

        //每日统计
        AppMachOilRecordDTO oilStat = appMachOilRecordMapper.selectOilAddStatByMachIdAndTimeRange(machId, startTime, endTime);
        Double oilAddManual = oilStat != null ? oilStat.getOilAddManual() : 0D;

        int days = dayOilList.size();
        MachRunDataDTO machRunDataDTO = new MachRunDataDTO();
        machRunDataDTO.setOilWear(MathUtil.round(oilWearTotal, 2));
        machRunDataDTO.setOilAdd(MathUtil.round(oilAddTotal, 2));
        machRunDataDTO.setAvgOilWear(MathUtil.round(1.0 * oilWearTotal / days, 2));
        machRunDataDTO.setAvgOilAdd(MathUtil.round(1.0 * oilAddTotal / days, 2));
        machRunDataDTO.setDayOilList(dayOilList);
        machRunDataDTO.setRemainOilList(remainOilList);
        machRunDataDTO.setOilAddManual(oilAddManual);
        machRunDataDTO.setSetOilAdd(0D);
        machRunDataDTO.setAvgSetOilAdd(0D);

        return machRunDataDTO;
    }

    @Override
    public MachRunDataDTO getOilData(Integer machId, Date date) throws BizException {
        logger.info("获取设备油耗,machId:{},date:{}", machId, date);
        //查找设备的基本信息
        AppMachInfo machInfo = this.getAndcheckMachOilTank(machId);

        //油位曲线
        Date startTime = DateUtil.getDateBegin(date);
        Date endTime = DateUtil.getDateEnd(date);
        List<Oil> oilList = machDataLogManager.getMachOilByTime(machId, startTime, endTime);
        List<Oil> remainOilList = machMgr.computeRemainOil(machInfo, oilList);

        //每日油耗
        List<OilDay> dayOilList = appMachDayMapper.selectOilDayListByMachId(machId, date, date);

        //每日统计
        AppMachOilRecordDTO oilStat = appMachOilRecordMapper.selectOilAddStatByMachIdAndTimeRange(machId, startTime, endTime);
        Double oilAddManual = oilStat != null ? oilStat.getOilAddManual() : 0D;

        //总油耗
        Double oilWearTotal = 0D;
        Double oilAddTotal = 0D;
        for (OilDay oilDay : dayOilList) {
            oilWearTotal += oilDay.getOilWear();
            oilAddTotal += oilDay.getOilAdd();
        }

        MachRunDataDTO machRunDataDTO = new MachRunDataDTO();
        machRunDataDTO.setOilWear(MathUtil.round(oilWearTotal, 2));
        machRunDataDTO.setOilAdd(MathUtil.round(oilAddTotal, 2));
        machRunDataDTO.setAvgOilWear(machRunDataDTO.getOilWear());
        machRunDataDTO.setAvgOilAdd(machRunDataDTO.getOilAdd());
        machRunDataDTO.setDayOilList(dayOilList);
        machRunDataDTO.setRemainOilList(remainOilList);
        machRunDataDTO.setOilAddManual(oilAddManual);
        machRunDataDTO.setSetOilAdd(0D);
        machRunDataDTO.setAvgSetOilAdd(0D);
        return machRunDataDTO;
    }

    @Override
    public Oil getRemainOil(Integer machId) {
        AppMachData machData = appMachDataMapper.selectByMachId(machId);
        if (machData == null) {
            throw new BizException(ResultEnum.FAILURE.getCode(), "未获取到设备油位数据");
        }
        AppMachInfo machInfo = this.getAndcheckMachOilTank(machId);

        Oil oil = machMgr.computeRemainOil(machInfo, machData.getOilPos());
        return oil;
    }

    @Override
    public ListData<Oil> oilPos(Integer machId, Date date) {
        logger.info("获取设备油位信息(开放接口)服务,machId:{},date:{}", machId, date);

        AppMachInfo machInfo = this.getAndcheckMachOilTank(machId);

        List<Oil> logList = machDataLogManager.getMachOilByDate(machId, date);

        machMgr.computeRemainOil(machInfo, logList);

        return new ListData<>(logList);
    }


    @Override
    public MachRadarMapDTO getRadarData(Integer machId, Date startDate, Date endDate) {
        logger.info("查询一台设备一段时间的运行指数服务,machId:{},startDate:{},endDate:{}", machId, startDate, endDate);
        MachRadarMapDTO radarMapDTO = appMachDayMapper.selectSingleMachRadarMap(machId, startDate, endDate);
        return radarMapDTO;
    }

    @Override
    public MachRadarMapDTO getSingleRardarMap(Integer machId, Date startDate, Date endDate) {
        logger.info("获取单机雷达图数据服务,machId:{},startDate:{},endDate:{}", machId, startDate, endDate);
        MachRadarMapDTO radarMapDTO = appMachDayMapper.selectSingleMachRadarMap(machId, startDate, endDate);
        return radarMapDTO;
    }

    @Override
    public MachRadarMapDTO getMultiRardarMap(List<Integer> deptIds) {
        logger.info("获取集群雷达图数据服务,deptIds:{}", deptIds);
        // 昨天的雷达图数据
        Date today = DateUtil.getDateBegin(new Date());
        Date yesterday = DateUtil.addDays(today, -1);
        MachRadarMapDTO radarMapDTO = appMachDayMapper.selectMultiMachRadarMap(deptIds, yesterday, yesterday);
        return radarMapDTO;
    }


    @Override
    public MachCurveMapDTO getMultiCurveMap(List<Integer> deptIds) {
        logger.info("获取集群曲线图数据服务,deptIds:{}", deptIds);
        // 7天数据统计
        Date today = DateUtil.getDateBegin(new Date());
        Date startDate = DateUtil.addDays(today, -7);
        Date endDate = DateUtil.addDays(today, -1);
        MachCurveMapDTO curveMapDTO = appMachDayMapper.selectMultiMachCurveMap(deptIds, startDate, endDate);
        // 总数
        Integer totalMachNum = appMachDeviceMapper.countTotalMachNum(deptIds);
        // 静止
        Integer stopMachNum1 = appMachDataMapper.countMachNumByWorkState(deptIds, WorkState.STOP.getValue());
        // 殆机
        Integer idleMachNum = appMachDataMapper.countMachNumByWorkState(deptIds, WorkState.IDLE.getValue());
        // 休眠
        Integer stopMachNum2 = appMachDataMapper.countMachNumByWorkState(deptIds, WorkState.SLEEP.getValue());
        // 错误
        Integer stopMachNum3 = appMachDataMapper.countMachNumByWorkState(deptIds, WorkState.ERROR.getValue());
        // 错误休眠归静止都为停机
        Integer stopMachNum = stopMachNum1 + stopMachNum2 + stopMachNum3;
        // 工作
        Integer workMachNum = appMachDataMapper.countMachNumByWorkState(deptIds, WorkState.RUN.getValue());
        Integer offlineMachNum = totalMachNum - stopMachNum - idleMachNum - workMachNum;

        curveMapDTO.setTotalMachNum(totalMachNum);
        curveMapDTO.setWorkMachNum(workMachNum);
        curveMapDTO.setIdleMachNum(idleMachNum);
        curveMapDTO.setOfflineMachNum(offlineMachNum);
        curveMapDTO.setStopMachNum(stopMachNum);
        // 每日数据统计
        List<MachCurveMapDayDTO> list = appMachDayMapper.selectMultiMachCurveMapDayList(deptIds, startDate, endDate);
        // 查找正常工作设备数
        for (MachCurveMapDayDTO wxMachCurveMapDayDTO : list) {
            Date dateBegin = DateUtil.getDateBegin(wxMachCurveMapDayDTO.getDate());
            Date dateEnd = DateUtil.getDateEnd(wxMachCurveMapDayDTO.getDate());
            Integer workNum = appMachDayMapper.countWorkMachNum(deptIds, dateBegin, dateEnd);
            wxMachCurveMapDayDTO.setWorkMachNum(workNum);
        }
        curveMapDTO.setList(list);

        return curveMapDTO;
    }

    @Override
    public ListData<MachCurveMapDayDTO> getSingleMachCurveMapDayList(Integer machId, Date startDate, Date endDate) {
        logger.info("获取单机一段时间内运行数据,machId:{},startDate:{},endDate:{}", machId, startDate, endDate);
        // 每日数据统计
        List<MachCurveMapDayDTO> list = appMachDayMapper.selectSingleMachCurveMapDayList(machId, startDate, endDate);
        return new ListData<>(list);
    }


    @Override
    public void refreshNetState() {
        logger.info("设备刷新设备硬件在线状态服务");
        Date time = new Date();
        List<Integer> machIds = appMachDataMapper.selectNetState2OfflineMachList();
        int count = appMachDataMapper.batchUpdateNetStateOffline(machIds);
        for (Integer machId : machIds) {
            // 增加离线数据点
            try {
                machDataManager.addOfflineData(machId, time);

            } catch (Exception e) {
                logger.error("添加离线数据点出错,machId:{},{}", machId, e);
                continue;
            }
        }

        logger.info("本次设备离线数量:{}", count);
    }

    @Override
    public void refreshObdNetState(List<Integer> deptIds) {
        logger.info("设备刷新obd硬件在线状态服务");
        Date time = new Date();
        List<Integer> machIds = appMachDataMapper.selectObdNetState2OfflineMachList(deptIds);
        int count = appMachDataMapper.batchUpdateObdNetStateOffline(machIds);
        for (Integer machId : machIds) {
            try {
                obdDataMgr.addOfflineData(machId, time);
            } catch (Exception e) {
                logger.error("添加离线数据点错误,machId:{},e:{}", machId, e);
                continue;
            }
        }
        logger.info("本次obd设备离线数量:{}", count);
    }

    @Override
    public void statMachDayData(Date date) {
        logger.info("统计每天设备数据服务,date:{}", date);
        int pageNum = 1;
        int pageSize = 20;
        int pages;
        do {
            PageHelper.startPage(pageNum, pageSize);
            List<AppMachInfo> machInfoList = appMachDeviceMapper.selectAppMachInfoList(DeviceType.mach.getValue());
            PageInfo<AppMachInfo> pageInfo = PageInfo.of(machInfoList);
            logger.info("task-statMachDayData,total:{},pages:{},pageNum:{},pageSize:{}", pageInfo.getTotal(), pageInfo.getPages(), pageNum, pageSize);
            pages = pageInfo.getPages();
            pageNum++;
            for (AppMachInfo machInfo : machInfoList) {
                machDataManager.statMachDayData(machInfo, date);
            }
        } while (pageNum <= pages);
    }

    @Override
    public void statMachDayData(Integer machId, Date date) {
        logger.info("统计某台设备每天数据服务,machId:{},date:{}", machId, date);
        AppMachInfo machInfo = appMachDeviceMapper.selectAppMachInfoByMachId(machId);
        machDataManager.statMachDayData(machInfo, date);
    }

    @Override
    public List<AppMachDataLogDTO> getMachDataLogCache(Integer machId, Date date) {
        logger.info("从缓存中查找设备数据服务,machId:{},date:{}", machId, date);
        List<AppMachDataLogDTO> machDataLogCache = machDataLogManager.getMachDataLogCache(machId, date);
        return machDataLogCache;
    }

    @Override
    public PageData<AppMachDataLogDTO> getMachDataLogListByDeviceId(Integer deviceId, Date startTime, Date endTime, Integer pageNum, Integer pageSize) throws BizException {
        logger.info("查找设备的硬件数据服务,deviceId:{},startTime:{},endTime:{},pageNum:{},pageSize:{}", deviceId, startTime, endTime, pageNum, pageSize);
        PageData<AppMachDataLogDTO> pageData = machDataLogManager.getDeviceDataLog(deviceId, startTime, endTime, pageNum, pageSize);
        return pageData;
    }

    @Override
    public void addMachDataLog(List<AppMachDataLog> dataLog) {
        logger.info("添加设备硬件数据服务,data:{}", dataLog.toString());
        machDataLogManager.addMachDataLog(dataLog);
    }

    @Override
    public void amendDeviceData(List<AppMachDataLog> dataList) {
        logger.info("修正设备硬件数据服务,param:{}", dataList.toString());
        machDataManager.amendDeviceData(dataList);
    }

    @Override
    public List<AppMachOilLogDTO> getDeviceOilLog(Integer deviceId, Date startTime, Date endTime) throws BizException {
        return appMachOilLogMapper.selectMachOilLogDTOList(deviceId, startTime, endTime);
    }

    @Override
    public List<AppMachOilSheetDTO> getMachOilSheet(Integer machId) throws BizException {
        return machMgr.getOilSheet(machId);
    }

    @Override
    public void saveMachOilSheet(Integer machId, List<AppMachOilSheetDTO> oilSheetDTOS) throws BizException {
        machMgr.refreshOilSheet(machId, oilSheetDTOS);
    }

    @Override
    public AppDeviceMachDataDTO getDeviceData(Integer deviceId) throws BizException {
        AppDeviceMachDataDTO dto = new AppDeviceMachDataDTO();
        AppDeviceMachData deviceData = appDeviceMachDataMapper.selectByDeviceId(deviceId);
        if (deviceData != null) {
            MapLoc location = mapApi.geocode(deviceData.getLng(), deviceData.getLat());
            WorkState workState = WorkState.parseByValue(deviceData.getWorkState());
            dto.setTime(deviceData.getTime());
            dto.setWorkState(workState.getDesc());
            dto.setOil(deviceData.getOilPos());
            dto.setLocation(location.getAddress());
        }
        return dto;
    }

    /**
     * 加上工时分段头部
     *
     * @param machDaySegDTOS
     */
    private void addHeadSegment(Integer machId, List<AppMachDaySegDTO> machDaySegDTOS, Date date) {
        Date yesterday = DateUtil.addDays(date, -1);
        Date dateBegin = DateUtil.getDateBegin(date);
        AppMachDaySegDTO appMachDaySegDTO = machDaySegDTOS.get(0);
        if (appMachDaySegDTO.getStartTime().before(dateBegin)) {
            appMachDaySegDTO.setStartTime(dateBegin);
            appMachDaySegDTO.setTimes(MachUtil.calculateTime(dateBegin, appMachDaySegDTO.getEndTime()));
            return;
        }

        // 前一天最后一个分段
        AppMachDaySegDTO lastMachDaySeg = machDataManager.getLastMachDaySeg(machId, yesterday);
        if (lastMachDaySeg != null) {
            if (NetState.OFFLINE.getValue().equals(lastMachDaySeg.getNetState())) {
                // 前一天最后一个点为离线,头部增加离线分段
                AppMachDaySegDTO data = MachSegmentUtil.buildMachDaySegment(dateBegin, appMachDaySegDTO.getStartTime(), null, NetState.OFFLINE.getValue());
                machDaySegDTOS.add(0, data);
            } else {
                // 前一天最后一个点不为离线,头部增加停机分段
                AppMachDaySegDTO data = MachSegmentUtil.buildMachDaySegment(dateBegin, appMachDaySegDTO.getStartTime(), WorkState.STOP.getValue(), null);
                machDaySegDTOS.add(0, data);
            }
        } else {
            // 前一天没有数据分段,头部增加离线分段
            AppMachDaySegDTO data = MachSegmentUtil.buildMachDaySegment(dateBegin, appMachDaySegDTO.getStartTime(), null, NetState.OFFLINE.getValue());
            machDaySegDTOS.add(0, data);
        }
    }

    /**
     * 加上工时分段尾部
     *
     * @param machDaySegDTOS
     */
    private void addTailSegment(Integer machId, List<AppMachDaySegDTO> machDaySegDTOS, Date date) {
        Date now = new Date();
        Date today = DateUtil.getDate(now);
        Date dateEnd = DateUtil.getDateEnd(date);
        Date tomorrow = DateUtil.addDays(date, 1);

        if (today.getTime() != date.getTime()) {
            // 历史数据加上尾部分段
            AppMachDaySegDTO lastMachDaySegDTO = machDaySegDTOS.get(machDaySegDTOS.size() - 1);
            if (lastMachDaySegDTO.getEndTime().after(dateEnd)) {
                lastMachDaySegDTO.setEndTime(dateEnd);
                lastMachDaySegDTO.setTimes(MachUtil.calculateTime(lastMachDaySegDTO.getStartTime(), dateEnd));
                return;
            }

            AppMachDaySegDTO firstMachDaySeg = machDataManager.getFirstMachDaySeg(machId, tomorrow);
            // 若果第二天第一个点或者今天最后一个点为离线则加上离线分段，否则加上停机分段
            if (firstMachDaySeg == null || NetState.OFFLINE.getValue().equals(lastMachDaySegDTO.getNetState()) || NetState.OFFLINE.getValue().equals(firstMachDaySeg.getNetState())) {
                AppMachDaySegDTO data = MachSegmentUtil.buildMachDaySegment(lastMachDaySegDTO.getEndTime(), dateEnd, null, NetState.OFFLINE.getValue());
                machDaySegDTOS.add(data);
            } else {
                AppMachDaySegDTO data = MachSegmentUtil.buildMachDaySegment(lastMachDaySegDTO.getEndTime(), dateEnd, WorkState.STOP.getValue(), null);
                machDaySegDTOS.add(data);
            }
        }
    }

    private Map<Integer, List<String>> list2Map(List<AppMachImg> machImgList) {
        Map<Integer, List<String>> map = new HashMap<>();
        for (AppMachImg appMachImg : machImgList) {
            String imgUrl = appMachImg.getImgUrl();
            Integer machId = appMachImg.getMachId();
            List<String> list = map.get(machId);
            if (list == null) {
                list = new ArrayList<>();
                map.put(machId, list);
            }
            list.add(imgUrl);
        }
        return map;
    }

    private Map<Integer, String> list3Map(List<AppCorpDTO> appCorpDTOS) {
        Map<Integer, String> map = new HashMap<>();
        for (AppCorpDTO appCorpDTO : appCorpDTOS) {
            Integer corpId = appCorpDTO.getCorpId();
            String corpName = appCorpDTO.getCorpName();
            map.put(corpId, corpName);
        }
        return map;
    }

    /**
     * 获取校验设备油箱设置
     *
     * @param machId
     * @return
     */
    private AppMachInfo getAndcheckMachOilTank(Integer machId) {
        AppMachInfo machInfo = appMachDeviceMapper.selectAppMachInfoByMachId(machId);
        if (machInfo == null) {
            throw new BizException(ResultEnum.FAILURE.getCode(), "未找到该设备");
        }
        Double volume = machInfo.getOilTankVolume();
        Double height = machInfo.getOilTankHeight();
        if (volume == null || volume <= 0 || height == null || height <= 0) {
            throw new BizException(ResultEnum.FAILURE.getCode(), "暂无油量数据。请在设备信息页面，补充油箱信息");
        }
        return machInfo;
    }
}
